#include <bits/stdc++.h>
using namespace std;

/*
2294. 划分数组使最大差为 K
已解答
中等
相关标签
premium lock icon
相关企业
提示
给你一个整数数组 nums 和一个整数 k 。你可以将 nums 划分成一个或多个 子序列 ，使 nums 中的每个元素都 恰好 出现在一个子序列中。

在满足每个子序列中最大值和最小值之间的差值最多为 k 的前提下，返回需要划分的 最少 子序列数目。

子序列 本质是一个序列，可以通过删除另一个序列中的某些元素（或者不删除）但不改变剩下元素的顺序得到。

 

示例 1：

输入：nums = [3,6,1,2,5], k = 2
输出：2
解释：
可以将 nums 划分为两个子序列 [3,1,2] 和 [6,5] 。
第一个子序列中最大值和最小值的差值是 3 - 1 = 2 。
第二个子序列中最大值和最小值的差值是 6 - 5 = 1 。
由于创建了两个子序列，返回 2 。可以证明需要划分的最少子序列数目就是 2 。
示例 2：

输入：nums = [1,2,3], k = 1
输出：2
解释：
可以将 nums 划分为两个子序列 [1,2] 和 [3] 。
第一个子序列中最大值和最小值的差值是 2 - 1 = 1 。
第二个子序列中最大值和最小值的差值是 3 - 3 = 0 。
由于创建了两个子序列，返回 2 。注意，另一种最优解法是将 nums 划分成子序列 [1] 和 [2,3] 。
示例 3：

输入：nums = [2,2,4,5], k = 0
输出：3
解释：
可以将 nums 划分为三个子序列 [2,2]、[4] 和 [5] 。
第一个子序列中最大值和最小值的差值是 2 - 2 = 0 。
第二个子序列中最大值和最小值的差值是 4 - 4 = 0 。
第三个子序列中最大值和最小值的差值是 5 - 5 = 0 。
由于创建了三个子序列，返回 3 。可以证明需要划分的最少子序列数目就是 3 。
 

提示：

1 <= nums.length <= 105
0 <= nums[i] <= 105
0 <= k <= 105
*/

// 法一
class Solution {
public:
    int partitionArray(vector<int>& nums, int k) {
        // 空容器
        if (nums.empty())   return 0;
        // sort排序
        sort(nums.begin(), nums.end());

        int minVal = nums[0];
        int count = 1;

        for (int num : nums) {
            if (num - minVal > k) {
                count++;
                minVal = num;
            }
        }
        return count;
    }
};

// 法二
class Solution {
public:
    int partitionArray(vector<int>& nums, int k) {
        // 处理空数组的情况
        if (nums.size() == 0)
            return 0;

        // 初始化最大值和最小值
        int mx = 0, mn = 1e5; // mn初始化为一个较大的值（1e5）
        for (int& n : nums) {
            mx = max(mx, n); // 更新最大值
            mn = min(mn, n); // 更新最小值
        }

        // 创建一个布尔数组a，用于标记nums中存在的数字
        // 数组大小为mx - mn + 2，确保能覆盖所有可能的数字
        bool a[mx - mn + 2];
        mx -= mn;                // 调整mx为相对于mn的偏移量
        memset(a, 0, sizeof(a)); // 初始化数组a为全0

        // 标记nums中存在的数字
        for (int& n : nums)
            a[n - mn] = 1; // n-mn将数字映射到数组a的索引

        int l = 0;   // 当前子序列的起始位置（相对于mn的偏移量）
        int ans = 1; // 最少需要的子序列数目，初始为1

        // 遍历数组a，检查每个存在的数字
        for (int i = 0; i <= mx; i++) {
            // 如果数字存在且与当前子序列起始位置的差超过k
            if (a[i] && i - l > k) {
                l = i; // 开启一个新的子序列
                ans++; // 子序列数目加1
            }
        }
        return ans; // 返回最少子序列数目
    }
};